Introduction

The metamoRph_label.R script was used to “auto label” all of the conjunctiva, cornea, lens, retina, rpe, and trabecular meshwork samples in the EiaD. Why not optic nerve? Because the others have at least two studies of samples.

The rough approach of metamoRph_label.R is to make make a subsample of the EiaD dataset (tissues listed above, with 10% randomly chosen tissues for each sample type (at least 5)) and:

  1. run pca on the samples
  2. use the PCs to train a lm / regression classifier for each tissue type
  3. project the full dataset by multiplying the counts against the rotation matrix
  4. use the trained model to label the projected dataset

Then do that 20 times. This “bootstrapping” approach is designed to not robustly not overfit the models.

The labels and scoring are imported here so I can hand assess the ML labels and identify problematic samples to remove.

The assessments are doing via running a PCA on all of the ocular samples (minus the AMD perturbed ones) and looking for how the ML regression scores (a higher minimum_score means the ML model has lower confidence in the call) and predicted labels vary across the dataset. I then hand assess each potential outlier in the context of their full metadata.

library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.2     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.2     ✔ tibble    3.2.1
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     ── Conflicts ───────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
# remotes::install_github("davemcg/metamoRph")
library(metamoRph)
library(data.table)
data.table 1.14.8 using 6 threads (see ?getDTthreads).  Latest news: r-datatable.com

Attaching package: ‘data.table’

The following objects are masked from ‘package:lubridate’:

    hour, isoweek, mday, minute, month, quarter, second, wday, week, yday, year

The following objects are masked from ‘package:dplyr’:

    between, first, last

The following object is masked from ‘package:purrr’:

    transpose
# this file generated by scripts/pca_workup_data_prep.R
mat_all <- vroom::vroom("~/git/EiaD_build/counts/gene_counts.csv.gz") %>% data.table() %>% 
  filter(!grepl("ENST", Gene)) %>% 
  mutate(Gene = gsub(" \\(.*","",Gene)) #%>% 
Rows: 37951 Columns: 2518── Column specification ───────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr    (1): Gene
dbl (2517): X105785, X105776, X105787, X105786, X105784, X105777, X105782, X105780, X105783, X105779, X...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
mat_all <- mat_all[, lapply(.SD, sum, na.rm=TRUE), by='Gene' ]
genes <- mat_all$Gene
mat_all <- mat_all[,-1] %>% as.matrix()
row.names(mat_all) <- genes
mat_all[1:10,1:10]
         X105785  X105776  X105787  X105786  X105784  X105777  X105782  X105780  X105783  X105779
A1BG     464.832  384.126  690.901  399.524  460.425  305.097  417.539  437.844  406.081  333.443
A1CF       4.000    0.000    0.000    0.000    0.000    3.000    0.000    1.000    2.000    0.000
A2M      119.000  149.000  170.000  104.000  428.000  277.000  251.000  305.000  615.000   44.000
A2ML1     79.580   63.658   59.888   52.597   63.026   76.072   49.136   58.488   48.945   30.350
A2MP1      0.000    0.000    0.000    0.000    0.000    0.000    0.000    0.000    0.000    0.000
A3GALT2    0.000    0.000    3.000    1.000    0.000    0.000    0.000    0.000    0.000    0.000
A4GALT   523.070  296.013  575.462  376.594  255.000  228.999  388.259  294.158  336.326  257.298
A4GNT      0.000    0.000    0.000    0.000    0.000    0.000    0.000    0.000    0.000    0.000
AAAS    2271.998 2064.000 2979.001 2025.001 1719.001 1909.000 2406.000 1994.999 2085.000 2405.999
AACS    1562.418 1603.641 2041.813 1742.944 1207.900 1311.544 1706.899 1360.067 1439.022 1346.006
emeta <- data.table::fread('../data/eyeIntegration22_meta_2023_03_03.csv.gz')

# from script/eigenProjecR_label.R
predictions <- read_tsv('../data/2023_08_12_ML_tissue_predictions.tsv.gz')
Rows: 783 Columns: 7── Column specification ───────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): sample_id, sample_label, predict
dbl (4): miscall_count, mean_max_score, min, max
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
pred_values <- read_tsv('../data/2023_08_12_ML_tissue_values.tsv.gz')
Rows: 783 Columns: 10── Column specification ───────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (3): sample_id, sample_label, predict
dbl (7): mean_max_score, Conjunctiva, Cornea, Lens, Retina, RPE, Trabecular.Meshwork
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
tissues <- c("Conjunctiva", "Cornea", "Lens", "Retina", "RPE", "Trabecular Meshwork")

Stats

emeta_filter <- emeta %>% 
  as_tibble() %>% 
  filter(study_accession != 'SRP012682') %>% 
  filter(Tissue %in% tissues) %>% 
  filter(!grepl("AMD", Perturbation)) %>% 
  select(sample_accession:study_title,Source_details ) %>% 
  unique()

emeta_filter %>% 
  mutate(Perturbation = case_when(grepl('MGS', Source_details) ~ Source_details),
         Perturbation = gsub("Adult Tissue","", Perturbation)) %>% 
  mutate(Sub_Tissue = case_when(is.na(Sub_Tissue) ~ '', TRUE ~ Sub_Tissue), 
         Source = case_when(is.na(Source) ~ '', TRUE ~ Source), 
         Age = case_when(is.na(Age) ~ '', TRUE ~ Age),
         Perturbation = case_when(is.na(Perturbation) ~ '', TRUE ~ Perturbation)) %>% 
  group_by(Tissue, Source, Sub_Tissue, Age, Perturbation, study_accession) %>% 
  summarise(`Study Count` = length(unique(study_accession)),
            `Sample Count` = n()) %>% 
  mutate(Tissue = case_when(grepl("Trab",Tissue) ~ 'TM', grepl("EyeLid", Tissue) ~ "Eye Lid", TRUE ~ Tissue),
         Source = case_when(grepl("Primary", Source) ~ "P. Culture",
                            TRUE ~ Source)
  ) %>% 
  ggplot(aes(x ='', y=`Sample Count`, fill = study_accession)) + 
  ggh4x::facet_nested(Tissue+Source+Sub_Tissue+Age ~ ., scale = 'free', space=  'free', switch = 'y',
                      strip = ggh4x::strip_nested(size = "variable"))+ 
  geom_bar(stat = 'identity') +
  xlab('') + 
  coord_flip()  + 
  theme_minimal() + 
  theme(strip.text.y.left = element_text(angle = 0, size = 12), text = element_text(size = 16))  + 
  theme(strip.background = element_rect(colour="gray", fill="gray"),
        strip.switch.pad.wrap = margin(t = 0, r = 0, b = 0, l = 0)) +
  theme(legend.position = "none") +
  scale_fill_manual(values = c(pals::alphabet() , pals::alphabet2(), pals::glasbey()) %>% unname())
`summarise()` has grouped output by 'Tissue', 'Source', 'Sub_Tissue', 'Age', 'Perturbation'. You can override using the `.groups` argument.

Top level look

Run PCA

Data built from scripts/pca_workup_data_prep.R and run on the non-AMD perturbed ocular bulk RNA-seq samples.

library(metamoRph)
pca_eiad <- run_pca(mat_all[,(emeta_filter %>% pull(sample_accession))], emeta_filter)
Sample CPM scaling
log1p scaling
prcomp start
PCA <- pca_eiad[[1]]
dataGG <- cbind(PCA$x, pca_eiad[[2]])
percentVar <- pca_eiad[[3]]

pcFirst <- 'PC1'
pcSecond <- 'PC2'
rotations <- c(pcFirst, pcSecond)

dataGG %>%
  #filter(Source != 'scRNA') %>% 
  as_tibble() %>% 
  mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                            Cohort == 'Body' ~ 'Body',
                            TRUE ~ Tissue)) %>% 
  ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]])) +
  geom_point(size=1, aes(color=Tissue, shape = Source)) +
  xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% as.integer()],"% variance")) +
  ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% as.integer()],"% variance")) + 
  cowplot::theme_cowplot() +
  scale_color_manual(values = c(pals::glasbey(), pals::alphabet2(), pals::alphabet2()) %>% unname()) +
  scale_fill_manual(values = c(pals::glasbey(), pals::alphabet2(), pals::alphabet2()) %>% unname()) +
  scale_shape_manual(values = 0:10) 

Reactive plot

library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
p <- dataGG %>%
  left_join(predictions, by = c("sample_accession" = "sample_id")) %>% 
  as_tibble() %>% 
  mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                            Cohort == 'Body' ~ 'Body',
                            TRUE ~ Tissue),
         Mislabel = case_when(sample_label != predict ~ 'Yes', TRUE ~ 'No'),
         Label = paste(Mislabel, sample_accession, Sub_Tissue, Source, Age, sep = '\n')) %>%
  ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]], label = Label)) +
  geom_point(size=1, aes(color=Tissue, shape = Source)) +
  xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% as.integer()],"% variance")) +
  ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% as.integer()],"% variance")) + 
  cowplot::theme_cowplot() +
  scale_color_manual(values = c(pals::glasbey(), pals::alphabet2(), pals::alphabet2()) %>% unname()) +
  scale_fill_manual(values = c(pals::glasbey(), pals::alphabet2(), pals::alphabet2()) %>% unname()) +
  scale_shape_manual(values = 0:10)
ggplotly(p)

Split by Tissue and Color by Min Score

Where higher values (near 1) mean higher confidence in the label assignment

PC1 and PC2

pcFirst <- 'PC1'
pcSecond <- 'PC2'
dataGG %>%
  filter(Tissue %in% tissues) %>% 
  as_tibble() %>% 
  mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                            Cohort == 'Body' ~ 'Body',
                            TRUE ~ Tissue)) %>% 
  left_join(pred_values, by = c('sample_accession' = 'sample_id')) %>% 
  mutate(mean_max_score = case_when(mean_max_score > 1 ~ 1, TRUE ~ mean_max_score)) %>% 
  ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]])) +
  geom_point(size=3, aes(color=mean_max_score, shape = Source)) +
  xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% as.integer()],"% variance")) +
  ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% as.integer()],"% variance")) + 
  cowplot::theme_cowplot() +
  scale_color_viridis_c() +
  scale_shape_manual(values = 0:10) + facet_wrap(~Tissue)

PC3 and PC4

pcFirst <- 'PC3'
pcSecond <- 'PC4'
dataGG %>%
  filter(Tissue %in% tissues) %>% 
  as_tibble() %>% 
  mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                            Cohort == 'Body' ~ 'Body',
                            TRUE ~ Tissue)) %>% 
  left_join(pred_values, by = c('sample_accession' = 'sample_id')) %>% 
  mutate(mean_max_score = case_when(mean_max_score > 1 ~ 1, TRUE ~ mean_max_score)) %>% 
  ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]])) +
  geom_point(size=3, aes(color=mean_max_score, shape = Source)) +
  xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% as.integer()],"% variance")) +
  ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% as.integer()],"% variance")) + 
  cowplot::theme_cowplot() +
  scale_color_viridis_c() +
  scale_shape_manual(values = 0:10) + facet_wrap(~Tissue)

Split by Tissue and Color by ML mislabel

Where lower values are more confident in the metamoRph tissue score

“Suspicious” samples are idenfied by three criteria:

  1. Predicted label does not match true (?) label
  2. The best ML score is above 0.5 (hand picked by looking at distribution of minimum ML score)
  3. If the ensemble of incorrect predicted labels is more than half

sus <- predictions %>% filter(sample_label != predict | mean_max_score < 0.7 | miscall_count > 9)

for (i in c(1,3,5,7,9)){
  pcFirst <- paste0('PC', i)
  pcSecond <- paste0('PC', i+1)
  
  
  print(dataGG %>%
          filter(Tissue %in% tissues) %>% 
          as_tibble() %>% 
          mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                                    Cohort == 'Body' ~ 'Body',
                                    TRUE ~ Tissue)) %>% 
          mutate(Suspicious = case_when(sample_accession %in% sus$sample_id ~ 'Yes')) %>% 
          ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]])) +
          geom_point(size=3, aes(color=Suspicious, shape = Source)) +
          xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% 
                                                 as.integer()],"% variance")) +
          ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% 
                                                  as.integer()],"% variance")) + 
          cowplot::theme_cowplot() +
          scale_shape_manual(values = 0:10) + facet_wrap(~Tissue)
  )}

One plot per study with any potential mis labels

library(ComplexHeatmap)
Loading required package: grid
========================================
ComplexHeatmap version 2.16.0
Bioconductor page: http://bioconductor.org/packages/ComplexHeatmap/
Github page: https://github.com/jokergoo/ComplexHeatmap
Documentation: http://jokergoo.github.io/ComplexHeatmap-reference

If you use it in published research, please cite either one:
- Gu, Z. Complex Heatmap Visualization. iMeta 2022.
- Gu, Z. Complex heatmaps reveal patterns and correlations in multidimensional 
    genomic data. Bioinformatics 2016.


The new InteractiveComplexHeatmap package can directly export static 
complex heatmaps into an interactive Shiny app with zero effort. Have a try!

This message can be suppressed by:
  suppressPackageStartupMessages(library(ComplexHeatmap))
========================================


Attaching package: ‘ComplexHeatmap’

The following object is masked from ‘package:plotly’:

    add_heatmap
studies_with_sus <- emeta %>% filter(sample_accession %in% sus$sample_id) %>% pull(study_accession) %>% unique()

full_df <- cbind(predictions, pred_values %>% select(Conjunctiva:`Trabecular.Meshwork`)) 
for (i in studies_with_sus){
  #print(i)
  hm_df <- left_join(predictions, pred_values %>% select(sample_id, Conjunctiva:`Trabecular.Meshwork`) %>% unique(), by = 'sample_id') %>% 
    left_join(emeta %>% select(sample_id = sample_accession, study_accession, Sub_Tissue, Source, Age), by ='sample_id') %>% 
    filter(study_accession %in% i) %>% 
    data.frame() %>% unique()
  row.names(hm_df) <- hm_df$sample_id
  
  
  ha_side = rowAnnotation(df = data.frame(ScientistLabel = 
                                            hm_df$sample_label %>% factor(levels = unique(full_df$sample_label)),
                                          eigenLabel = 
                                            hm_df$predict %>% factor(levels = unique(full_df$sample_label)),
                                          Source = 
                                            hm_df$Source %>% factor(levels = unique(hm_df$Source)),
                                          Age = 
                                            hm_df$Age %>% factor(levels = unique(hm_df$Age))),
                          col = list(ScientistLabel = c("Conjunctiva" = pals::alphabet2(20)[2] %>% unname(), 
                                                        "Cornea" = pals::alphabet2(20)[3] %>% unname(),
                                                        "Lens" = pals::alphabet2(20)[6] %>% unname(),
                                                        "Retina" = pals::alphabet2(20)[10] %>% unname(),
                                                        "RPE" = pals::alphabet2(20)[13] %>% unname(),
                                                        "Trabecular Meshwork" = pals::alphabet2(20)[16] %>% unname()),
                                     eigenLabel = c("Conjunctiva" = pals::alphabet2(20)[2] %>% unname(), 
                                                    "Cornea" = pals::alphabet2(20)[3] %>% unname(),
                                                    "Lens" = pals::alphabet2(20)[6] %>% unname(),
                                                    "Retina" = pals::alphabet2(20)[10] %>% unname(),
                                                    "RPE" = pals::alphabet2(20)[13] %>% unname(),
                                                    "Trabecular Meshwork" = pals::alphabet2(20)[16] %>% unname())))
  
  
  print(Heatmap(as.matrix(hm_df[,8:13]),  col=circlize::colorRamp2(c(0, 0.5, 1), c("blue", "white", "red")),
                right_annotation = ha_side, cluster_rows = FALSE, column_title = i))
  
  pcFirst <- "PC1"
  pcSecond <- 'PC2'
  print(dataGG %>%
          filter(study_accession == i, Tissue %in% tissues) %>% 
          as_tibble() %>% 
          mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                                    Cohort == 'Body' ~ 'Body',
                                    TRUE ~ Tissue)) %>% 
          mutate(Suspicious = case_when(sample_accession %in% sus$sample_id ~ 'Yes', TRUE ~ 'No')) %>% 
          ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]], label = sample_accession)) +
          geom_point(data = dataGG %>% 
                       filter(study_accession != i) %>% 
                       filter(Tissue %in% tissues),
                     size=2, 
                     aes(x =.data[[pcFirst]], y= .data[[pcSecond]]), color = 'grey' ) +
          geom_point(size=3, aes(color=Suspicious, shape = Source)) +
          
          ggrepel::geom_label_repel( size = 2.5, max.overlaps = Inf) +
          xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% 
                                                 as.integer()],"% variance")) +
          ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% 
                                                  as.integer()],"% variance")) + 
          cowplot::theme_cowplot() +
          scale_shape_manual(values = 0:10) + facet_wrap(~Tissue, ncol = 2)
  )
  
  pcFirst <- "PC3"
  pcSecond <- 'PC4'
  print(dataGG %>%
          filter(study_accession == i,
                 Tissue %in% tissues) %>% 
          as_tibble() %>% 
          mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                                    Cohort == 'Body' ~ 'Body',
                                    TRUE ~ Tissue)) %>% 
          mutate(Suspicious = case_when(sample_accession %in% sus$sample_id ~ 'Yes', TRUE ~ 'No')) %>% 
          ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]], label = sample_accession)) +
          geom_point(data = dataGG %>% 
                       filter(study_accession != i) %>% 
                       filter(Tissue %in% tissues), 
                     size=2, 
                     aes(x =.data[[pcFirst]], y= .data[[pcSecond]]), color = 'grey' ) +
          geom_point(size=3, aes(color=Suspicious, shape = Source)) +
          ggrepel::geom_label_repel(size = 2.5, max.overlaps = Inf) +
          xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% 
                                                 as.integer()],"% variance")) +
          ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% 
                                                  as.integer()],"% variance")) + 
          cowplot::theme_cowplot() +
          scale_shape_manual(values = 0:10) + facet_wrap(~Tissue, ncol = 2)
  )
}

Hand picked samples to remove

Picked by using the plots above and checking out the full metadata to see if there were any other explanations for what appear to be outliers (an example is SRP105756 which has many early fetal retina tissues or SRP097696 which has a surprising flow sorted retina which only keeps endothelial cells).

eigen_hand_removed <- c('SRS6509684',
                        'SRS390609',
                        'SRS542573','SRS542574',
                        'SRS1478834', #
                        'SRS8145606', #
                        'SRS360123','SRS360124', #
                        'SRS7504868','SRS7504869', #
                        'SRS523835','SRS523813',
                        'SRS1955494')

predictions %>% left_join(emeta %>% select(study_accession, sample_accession, Sub_Tissue, Source, Age), by = c('sample_id'='sample_accession')) %>% as_tibble() %>% filter(sample_id %in% eigen_hand_removed) %>% arrange(sample_label)

predictions %>% 
  left_join(emeta %>% ungroup() %>%  dplyr::select(study_accession, sample_accession, Sub_Tissue, Source, Age), 
            by = c('sample_id'='sample_accession')) %>% 
  as_tibble() %>% 
  filter(sample_id %in% eigen_hand_removed) %>% 
  arrange(sample_label) %>% group_by(sample_label, Sub_Tissue, Source, Age) %>% summarise(Count = n(), samples = paste0(sample_id, collapse = ', '))
`summarise()` has grouped output by 'sample_label', 'Sub_Tissue', 'Source'. You can override using the `.groups` argument.
for (i in c(1,3,5,7)){
  pcFirst <- paste0('PC', i)
  pcSecond <- paste0('PC', i+1)
  print(dataGG %>%
          as_tibble() %>% 
          mutate(Tissue = case_when(Tissue == 'Brain' ~ Tissue,
                                    Cohort == 'Body' ~ 'Body',
                                    TRUE ~ Tissue)) %>% 
          mutate(Removed = case_when(sample_accession %in% eigen_hand_removed ~ 'Yes')) %>% 
          ggplot(., aes(.data[[pcFirst]], .data[[pcSecond]])) +
          geom_point(size=1, aes(color=Removed, shape = Source)) +
          xlab(paste0(pcFirst, ": ",percentVar[str_extract(pcFirst, '\\d+') %>% as.integer()],"% variance")) +
          ylab(paste0(pcSecond, ": ",percentVar[str_extract(pcSecond, '\\d+') %>% as.integer()],"% variance")) + 
          cowplot::theme_cowplot() +
          scale_color_manual(values = c(pals::glasbey(), pals::alphabet2(), pals::alphabet2()) %>% unname()) +
          scale_fill_manual(values = c(pals::glasbey(), pals::alphabet2(), pals::alphabet2()) %>% unname()) +
          scale_shape_manual(values = 0:10) +
          facet_wrap(~Tissue)
  )
}

Output hand removed IDs

write(eigen_hand_removed, file = '../data/excluded_samples.txt')
devtools::session_info()
LS0tCnRpdGxlOiAiTGFiZWwgQW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIGF1dGhvcjogIkRhdmlkIE1jR2F1Z2hleSIKICAgIGRhdGU6ICIyMDIzLTA4LTEzIgogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgojIEludHJvZHVjdGlvbgoKVGhlIG1ldGFtb1JwaF9sYWJlbC5SIHNjcmlwdCB3YXMgdXNlZCB0byAiYXV0byBsYWJlbCIgYWxsIG9mIHRoZSBjb25qdW5jdGl2YSwKY29ybmVhLCBsZW5zLCByZXRpbmEsIHJwZSwgYW5kIHRyYWJlY3VsYXIgbWVzaHdvcmsgc2FtcGxlcyBpbiB0aGUgRWlhRC4gV2h5IG5vdCBvcHRpYyBuZXJ2ZT8gQmVjYXVzZSB0aGUgb3RoZXJzIGhhdmUgYXQgbGVhc3QgdHdvIHN0dWRpZXMgb2Ygc2FtcGxlcy4KClRoZSByb3VnaCBhcHByb2FjaCBvZiBtZXRhbW9ScGhfbGFiZWwuUiBpcyB0byBtYWtlIG1ha2UgYSBzdWJzYW1wbGUgb2YgdGhlIEVpYUQgZGF0YXNldCAodGlzc3VlcyBsaXN0ZWQgYWJvdmUsIHdpdGggMTAlIHJhbmRvbWx5IGNob3NlbiB0aXNzdWVzIGZvciBlYWNoIHNhbXBsZSB0eXBlIChhdCBsZWFzdCA1KSkgYW5kOgoKMS4gcnVuIHBjYSBvbiB0aGUgc2FtcGxlcwoyLiB1c2UgdGhlIFBDcyB0byB0cmFpbiBhIGxtIC8gcmVncmVzc2lvbiBjbGFzc2lmaWVyIGZvciBlYWNoIHRpc3N1ZSB0eXBlCjMuIHByb2plY3QgdGhlIGZ1bGwgZGF0YXNldCBieSBtdWx0aXBseWluZyB0aGUgY291bnRzIGFnYWluc3QgdGhlIHJvdGF0aW9uIG1hdHJpeAo0LiB1c2UgdGhlIHRyYWluZWQgbW9kZWwgdG8gbGFiZWwgdGhlIHByb2plY3RlZCBkYXRhc2V0CgpUaGVuIGRvIHRoYXQgMjAgdGltZXMuIFRoaXMgImJvb3RzdHJhcHBpbmciIGFwcHJvYWNoIGlzIGRlc2lnbmVkIHRvIG5vdCByb2J1c3RseSAqbm90KiBvdmVyZml0IHRoZSBtb2RlbHMuCgpUaGUgbGFiZWxzIGFuZCBzY29yaW5nIGFyZSBpbXBvcnRlZCBoZXJlIHNvIEkgY2FuIGhhbmQgYXNzZXNzIHRoZSBNTCBsYWJlbHMgYW5kIGlkZW50aWZ5IHByb2JsZW1hdGljIHNhbXBsZXMgdG8gcmVtb3ZlLgoKVGhlIGFzc2Vzc21lbnRzIGFyZSBkb2luZyB2aWEgcnVubmluZyBhIFBDQSBvbiBhbGwgb2YgdGhlIG9jdWxhciBzYW1wbGVzIChtaW51cyB0aGUgQU1EIHBlcnR1cmJlZCBvbmVzKQphbmQgbG9va2luZyBmb3IgaG93IHRoZSBNTCByZWdyZXNzaW9uIHNjb3JlcyAoYSBoaWdoZXIgbWluaW11bV9zY29yZSBtZWFucyB0aGUgTUwgbW9kZWwgaGFzIGxvd2VyIGNvbmZpZGVuY2UKaW4gdGhlIGNhbGwpIGFuZCBwcmVkaWN0ZWQgbGFiZWxzIHZhcnkgYWNyb3NzIHRoZSBkYXRhc2V0LiBJIHRoZW4gaGFuZCBhc3Nlc3MgZWFjaCBwb3RlbnRpYWwgb3V0bGllciBpbiB0aGUgY29udGV4dCBvZiB0aGVpciBmdWxsIG1ldGFkYXRhLgoKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZGF2ZW1jZy9tZXRhbW9ScGgiKQpsaWJyYXJ5KG1ldGFtb1JwaCkKbGlicmFyeShkYXRhLnRhYmxlKQojIHRoaXMgZmlsZSBnZW5lcmF0ZWQgYnkgc2NyaXB0cy9wY2Ffd29ya3VwX2RhdGFfcHJlcC5SCm1hdF9hbGwgPC0gdnJvb206OnZyb29tKCJ+L2dpdC9FaWFEX2J1aWxkL2NvdW50cy9nZW5lX2NvdW50cy5jc3YuZ3oiKSAlPiUgZGF0YS50YWJsZSgpICU+JSAKICBmaWx0ZXIoIWdyZXBsKCJFTlNUIiwgR2VuZSkpICU+JSAKICBtdXRhdGUoR2VuZSA9IGdzdWIoIiBcXCguKiIsIiIsR2VuZSkpICMlPiUgCm1hdF9hbGwgPC0gbWF0X2FsbFssIGxhcHBseSguU0QsIHN1bSwgbmEucm09VFJVRSksIGJ5PSdHZW5lJyBdCmdlbmVzIDwtIG1hdF9hbGwkR2VuZQptYXRfYWxsIDwtIG1hdF9hbGxbLC0xXSAlPiUgYXMubWF0cml4KCkKcm93Lm5hbWVzKG1hdF9hbGwpIDwtIGdlbmVzCm1hdF9hbGxbMToxMCwxOjEwXQoKZW1ldGEgPC0gZGF0YS50YWJsZTo6ZnJlYWQoJy4uL2RhdGEvZXllSW50ZWdyYXRpb24yMl9tZXRhXzIwMjNfMDNfMDMuY3N2Lmd6JykKCiMgZnJvbSBzY3JpcHQvZWlnZW5Qcm9qZWNSX2xhYmVsLlIKcHJlZGljdGlvbnMgPC0gcmVhZF90c3YoJy4uL2RhdGEvMjAyM18wOF8xMl9NTF90aXNzdWVfcHJlZGljdGlvbnMudHN2Lmd6JykKcHJlZF92YWx1ZXMgPC0gcmVhZF90c3YoJy4uL2RhdGEvMjAyM18wOF8xMl9NTF90aXNzdWVfdmFsdWVzLnRzdi5neicpCgp0aXNzdWVzIDwtIGMoIkNvbmp1bmN0aXZhIiwgIkNvcm5lYSIsICJMZW5zIiwgIlJldGluYSIsICJSUEUiLCAiVHJhYmVjdWxhciBNZXNod29yayIpCmBgYAojIFN0YXRzCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0xMH0KZW1ldGFfZmlsdGVyIDwtIGVtZXRhICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgZmlsdGVyKHN0dWR5X2FjY2Vzc2lvbiAhPSAnU1JQMDEyNjgyJykgJT4lIAogIGZpbHRlcihUaXNzdWUgJWluJSB0aXNzdWVzKSAlPiUgCiAgZmlsdGVyKCFncmVwbCgiQU1EIiwgUGVydHVyYmF0aW9uKSkgJT4lIAogIHNlbGVjdChzYW1wbGVfYWNjZXNzaW9uOnN0dWR5X3RpdGxlLFNvdXJjZV9kZXRhaWxzICkgJT4lIAogIHVuaXF1ZSgpCgplbWV0YV9maWx0ZXIgJT4lIAogIG11dGF0ZShQZXJ0dXJiYXRpb24gPSBjYXNlX3doZW4oZ3JlcGwoJ01HUycsIFNvdXJjZV9kZXRhaWxzKSB+IFNvdXJjZV9kZXRhaWxzKSwKICAgICAgICAgUGVydHVyYmF0aW9uID0gZ3N1YigiQWR1bHQgVGlzc3VlIiwiIiwgUGVydHVyYmF0aW9uKSkgJT4lIAogIG11dGF0ZShTdWJfVGlzc3VlID0gY2FzZV93aGVuKGlzLm5hKFN1Yl9UaXNzdWUpIH4gJycsIFRSVUUgfiBTdWJfVGlzc3VlKSwgCiAgICAgICAgIFNvdXJjZSA9IGNhc2Vfd2hlbihpcy5uYShTb3VyY2UpIH4gJycsIFRSVUUgfiBTb3VyY2UpLCAKICAgICAgICAgQWdlID0gY2FzZV93aGVuKGlzLm5hKEFnZSkgfiAnJywgVFJVRSB+IEFnZSksCiAgICAgICAgIFBlcnR1cmJhdGlvbiA9IGNhc2Vfd2hlbihpcy5uYShQZXJ0dXJiYXRpb24pIH4gJycsIFRSVUUgfiBQZXJ0dXJiYXRpb24pKSAlPiUgCiAgZ3JvdXBfYnkoVGlzc3VlLCBTb3VyY2UsIFN1Yl9UaXNzdWUsIEFnZSwgUGVydHVyYmF0aW9uLCBzdHVkeV9hY2Nlc3Npb24pICU+JSAKICBzdW1tYXJpc2UoYFN0dWR5IENvdW50YCA9IGxlbmd0aCh1bmlxdWUoc3R1ZHlfYWNjZXNzaW9uKSksCiAgICAgICAgICAgIGBTYW1wbGUgQ291bnRgID0gbigpKSAlPiUgCiAgbXV0YXRlKFRpc3N1ZSA9IGNhc2Vfd2hlbihncmVwbCgiVHJhYiIsVGlzc3VlKSB+ICdUTScsIGdyZXBsKCJFeWVMaWQiLCBUaXNzdWUpIH4gIkV5ZSBMaWQiLCBUUlVFIH4gVGlzc3VlKSwKICAgICAgICAgU291cmNlID0gY2FzZV93aGVuKGdyZXBsKCJQcmltYXJ5IiwgU291cmNlKSB+ICJQLiBDdWx0dXJlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBTb3VyY2UpCiAgKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0nJywgeT1gU2FtcGxlIENvdW50YCwgZmlsbCA9IHN0dWR5X2FjY2Vzc2lvbikpICsgCiAgZ2doNHg6OmZhY2V0X25lc3RlZChUaXNzdWUrU291cmNlK1N1Yl9UaXNzdWUrQWdlIH4gLiwgc2NhbGUgPSAnZnJlZScsIHNwYWNlPSAgJ2ZyZWUnLCBzd2l0Y2ggPSAneScsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpcCA9IGdnaDR4OjpzdHJpcF9uZXN0ZWQoc2l6ZSA9ICJ2YXJpYWJsZSIpKSsgCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICB4bGFiKCcnKSArIAogIGNvb3JkX2ZsaXAoKSAgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShzdHJpcC50ZXh0LnkubGVmdCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIHNpemUgPSAxMiksIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkgICsgCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXI9ImdyYXkiLCBmaWxsPSJncmF5IiksCiAgICAgICAgc3RyaXAuc3dpdGNoLnBhZC53cmFwID0gbWFyZ2luKHQgPSAwLCByID0gMCwgYiA9IDAsIGwgPSAwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMocGFsczo6YWxwaGFiZXQoKSAsIHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjpnbGFzYmV5KCkpICU+JSB1bm5hbWUoKSkKYGBgCgojIFRvcCBsZXZlbCBsb29rCiMjIFJ1biBQQ0EgCgpEYXRhIGJ1aWx0IGZyb20gc2NyaXB0cy9wY2Ffd29ya3VwX2RhdGFfcHJlcC5SIGFuZCBydW4gb24gdGhlIG5vbi1BTUQgcGVydHVyYmVkIG9jdWxhciBidWxrIFJOQS1zZXEgc2FtcGxlcy4KCmBgYHtyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD05fQpsaWJyYXJ5KG1ldGFtb1JwaCkKcGNhX2VpYWQgPC0gcnVuX3BjYShtYXRfYWxsWywoZW1ldGFfZmlsdGVyICU+JSBwdWxsKHNhbXBsZV9hY2Nlc3Npb24pKV0sIGVtZXRhX2ZpbHRlcikKClBDQSA8LSBwY2FfZWlhZFtbMV1dCmRhdGFHRyA8LSBjYmluZChQQ0EkeCwgcGNhX2VpYWRbWzJdXSkKcGVyY2VudFZhciA8LSBwY2FfZWlhZFtbM11dCgpwY0ZpcnN0IDwtICdQQzEnCnBjU2Vjb25kIDwtICdQQzInCnJvdGF0aW9ucyA8LSBjKHBjRmlyc3QsIHBjU2Vjb25kKQoKZGF0YUdHICU+JQogICNmaWx0ZXIoU291cmNlICE9ICdzY1JOQScpICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgbXV0YXRlKFRpc3N1ZSA9IGNhc2Vfd2hlbihUaXNzdWUgPT0gJ0JyYWluJyB+IFRpc3N1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvaG9ydCA9PSAnQm9keScgfiAnQm9keScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gVGlzc3VlKSkgJT4lIAogIGdncGxvdCguLCBhZXMoLmRhdGFbW3BjRmlyc3RdXSwgLmRhdGFbW3BjU2Vjb25kXV0pKSArCiAgZ2VvbV9wb2ludChzaXplPTEsIGFlcyhjb2xvcj1UaXNzdWUsIHNoYXBlID0gU291cmNlKSkgKwogIHhsYWIocGFzdGUwKHBjRmlyc3QsICI6ICIscGVyY2VudFZhcltzdHJfZXh0cmFjdChwY0ZpcnN0LCAnXFxkKycpICU+JSBhcy5pbnRlZ2VyKCldLCIlIHZhcmlhbmNlIikpICsKICB5bGFiKHBhc3RlMChwY1NlY29uZCwgIjogIixwZXJjZW50VmFyW3N0cl9leHRyYWN0KHBjU2Vjb25kLCAnXFxkKycpICU+JSBhcy5pbnRlZ2VyKCldLCIlIHZhcmlhbmNlIikpICsgCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjpnbGFzYmV5KCksIHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjphbHBoYWJldDIoKSkgJT4lIHVubmFtZSgpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhwYWxzOjpnbGFzYmV5KCksIHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjphbHBoYWJldDIoKSkgJT4lIHVubmFtZSgpKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IDA6MTApIApgYGAKCiMjIFJlYWN0aXZlIHBsb3QKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0KbGlicmFyeShwbG90bHkpCnAgPC0gZGF0YUdHICU+JQogIGxlZnRfam9pbihwcmVkaWN0aW9ucywgYnkgPSBjKCJzYW1wbGVfYWNjZXNzaW9uIiA9ICJzYW1wbGVfaWQiKSkgJT4lIAogIGFzX3RpYmJsZSgpICU+JSAKICBtdXRhdGUoVGlzc3VlID0gY2FzZV93aGVuKFRpc3N1ZSA9PSAnQnJhaW4nIH4gVGlzc3VlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29ob3J0ID09ICdCb2R5JyB+ICdCb2R5JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBUaXNzdWUpLAogICAgICAgICBNaXNsYWJlbCA9IGNhc2Vfd2hlbihzYW1wbGVfbGFiZWwgIT0gcHJlZGljdCB+ICdZZXMnLCBUUlVFIH4gJ05vJyksCiAgICAgICAgIExhYmVsID0gcGFzdGUoTWlzbGFiZWwsIHNhbXBsZV9hY2Nlc3Npb24sIFN1Yl9UaXNzdWUsIFNvdXJjZSwgQWdlLCBzZXAgPSAnXG4nKSkgJT4lCiAgZ2dwbG90KC4sIGFlcyguZGF0YVtbcGNGaXJzdF1dLCAuZGF0YVtbcGNTZWNvbmRdXSwgbGFiZWwgPSBMYWJlbCkpICsKICBnZW9tX3BvaW50KHNpemU9MSwgYWVzKGNvbG9yPVRpc3N1ZSwgc2hhcGUgPSBTb3VyY2UpKSArCiAgeGxhYihwYXN0ZTAocGNGaXJzdCwgIjogIixwZXJjZW50VmFyW3N0cl9leHRyYWN0KHBjRmlyc3QsICdcXGQrJykgJT4lIGFzLmludGVnZXIoKV0sIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKHBjU2Vjb25kLCAiOiAiLHBlcmNlbnRWYXJbc3RyX2V4dHJhY3QocGNTZWNvbmQsICdcXGQrJykgJT4lIGFzLmludGVnZXIoKV0sIiUgdmFyaWFuY2UiKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmdsYXNiZXkoKSwgcGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmFscGhhYmV0MigpKSAlPiUgdW5uYW1lKCkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmdsYXNiZXkoKSwgcGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmFscGhhYmV0MigpKSAlPiUgdW5uYW1lKCkpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gMDoxMCkKZ2dwbG90bHkocCkKYGBgCgoKIyBTcGxpdCBieSBUaXNzdWUgYW5kIENvbG9yIGJ5IE1pbiBTY29yZQpXaGVyZSBoaWdoZXIgdmFsdWVzIChuZWFyIDEpIG1lYW4gaGlnaGVyIGNvbmZpZGVuY2UgaW4gdGhlIGxhYmVsIGFzc2lnbm1lbnQKCiMjIFBDMSBhbmQgUEMyCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0KcGNGaXJzdCA8LSAnUEMxJwpwY1NlY29uZCA8LSAnUEMyJwpkYXRhR0cgJT4lCiAgZmlsdGVyKFRpc3N1ZSAlaW4lIHRpc3N1ZXMpICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgbXV0YXRlKFRpc3N1ZSA9IGNhc2Vfd2hlbihUaXNzdWUgPT0gJ0JyYWluJyB+IFRpc3N1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvaG9ydCA9PSAnQm9keScgfiAnQm9keScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gVGlzc3VlKSkgJT4lIAogIGxlZnRfam9pbihwcmVkX3ZhbHVlcywgYnkgPSBjKCdzYW1wbGVfYWNjZXNzaW9uJyA9ICdzYW1wbGVfaWQnKSkgJT4lIAogIG11dGF0ZShtZWFuX21heF9zY29yZSA9IGNhc2Vfd2hlbihtZWFuX21heF9zY29yZSA+IDEgfiAxLCBUUlVFIH4gbWVhbl9tYXhfc2NvcmUpKSAlPiUgCiAgZ2dwbG90KC4sIGFlcyguZGF0YVtbcGNGaXJzdF1dLCAuZGF0YVtbcGNTZWNvbmRdXSkpICsKICBnZW9tX3BvaW50KHNpemU9MywgYWVzKGNvbG9yPW1lYW5fbWF4X3Njb3JlLCBzaGFwZSA9IFNvdXJjZSkpICsKICB4bGFiKHBhc3RlMChwY0ZpcnN0LCAiOiAiLHBlcmNlbnRWYXJbc3RyX2V4dHJhY3QocGNGaXJzdCwgJ1xcZCsnKSAlPiUgYXMuaW50ZWdlcigpXSwiJSB2YXJpYW5jZSIpKSArCiAgeWxhYihwYXN0ZTAocGNTZWNvbmQsICI6ICIscGVyY2VudFZhcltzdHJfZXh0cmFjdChwY1NlY29uZCwgJ1xcZCsnKSAlPiUgYXMuaW50ZWdlcigpXSwiJSB2YXJpYW5jZSIpKSArIAogIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSAwOjEwKSArIGZhY2V0X3dyYXAoflRpc3N1ZSkKYGBgCgojIyBQQzMgYW5kIFBDNApgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9CnBjRmlyc3QgPC0gJ1BDMycKcGNTZWNvbmQgPC0gJ1BDNCcKZGF0YUdHICU+JQogIGZpbHRlcihUaXNzdWUgJWluJSB0aXNzdWVzKSAlPiUgCiAgYXNfdGliYmxlKCkgJT4lIAogIG11dGF0ZShUaXNzdWUgPSBjYXNlX3doZW4oVGlzc3VlID09ICdCcmFpbicgfiBUaXNzdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2hvcnQgPT0gJ0JvZHknIH4gJ0JvZHknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IFRpc3N1ZSkpICU+JSAKICBsZWZ0X2pvaW4ocHJlZF92YWx1ZXMsIGJ5ID0gYygnc2FtcGxlX2FjY2Vzc2lvbicgPSAnc2FtcGxlX2lkJykpICU+JSAKICBtdXRhdGUobWVhbl9tYXhfc2NvcmUgPSBjYXNlX3doZW4obWVhbl9tYXhfc2NvcmUgPiAxIH4gMSwgVFJVRSB+IG1lYW5fbWF4X3Njb3JlKSkgJT4lIAogIGdncGxvdCguLCBhZXMoLmRhdGFbW3BjRmlyc3RdXSwgLmRhdGFbW3BjU2Vjb25kXV0pKSArCiAgZ2VvbV9wb2ludChzaXplPTMsIGFlcyhjb2xvcj1tZWFuX21heF9zY29yZSwgc2hhcGUgPSBTb3VyY2UpKSArCiAgeGxhYihwYXN0ZTAocGNGaXJzdCwgIjogIixwZXJjZW50VmFyW3N0cl9leHRyYWN0KHBjRmlyc3QsICdcXGQrJykgJT4lIGFzLmludGVnZXIoKV0sIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKHBjU2Vjb25kLCAiOiAiLHBlcmNlbnRWYXJbc3RyX2V4dHJhY3QocGNTZWNvbmQsICdcXGQrJykgJT4lIGFzLmludGVnZXIoKV0sIiUgdmFyaWFuY2UiKSkgKyAKICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gMDoxMCkgKyBmYWNldF93cmFwKH5UaXNzdWUpCmBgYAoKIyBTcGxpdCBieSBUaXNzdWUgYW5kIENvbG9yIGJ5IE1MIG1pc2xhYmVsCldoZXJlIGxvd2VyIHZhbHVlcyBhcmUgbW9yZSBjb25maWRlbnQgaW4gdGhlIG1ldGFtb1JwaCB0aXNzdWUgc2NvcmUKCiJTdXNwaWNpb3VzIiBzYW1wbGVzIGFyZSBpZGVuZmllZCBieSB0aHJlZSBjcml0ZXJpYToKCjEuIFByZWRpY3RlZCBsYWJlbCBkb2VzIG5vdCBtYXRjaCB0cnVlICg/KSBsYWJlbAoyLiBUaGUgYmVzdCBNTCBzY29yZSBpcyBhYm92ZSAwLjUgKGhhbmQgcGlja2VkIGJ5IGxvb2tpbmcgYXQgZGlzdHJpYnV0aW9uIG9mIG1pbmltdW0gTUwgc2NvcmUpCjMuIElmIHRoZSBlbnNlbWJsZSBvZiBpbmNvcnJlY3QgcHJlZGljdGVkIGxhYmVscyBpcyBtb3JlIHRoYW4gaGFsZiAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQoKc3VzIDwtIHByZWRpY3Rpb25zICU+JSBmaWx0ZXIoc2FtcGxlX2xhYmVsICE9IHByZWRpY3QgfCBtZWFuX21heF9zY29yZSA8IDAuNyB8IG1pc2NhbGxfY291bnQgPiA5KQoKZm9yIChpIGluIGMoMSwzLDUsNyw5KSl7CiAgcGNGaXJzdCA8LSBwYXN0ZTAoJ1BDJywgaSkKICBwY1NlY29uZCA8LSBwYXN0ZTAoJ1BDJywgaSsxKQogIAogIAogIHByaW50KGRhdGFHRyAlPiUKICAgICAgICAgIGZpbHRlcihUaXNzdWUgJWluJSB0aXNzdWVzKSAlPiUgCiAgICAgICAgICBhc190aWJibGUoKSAlPiUgCiAgICAgICAgICBtdXRhdGUoVGlzc3VlID0gY2FzZV93aGVuKFRpc3N1ZSA9PSAnQnJhaW4nIH4gVGlzc3VlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2hvcnQgPT0gJ0JvZHknIH4gJ0JvZHknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gVGlzc3VlKSkgJT4lIAogICAgICAgICAgbXV0YXRlKFN1c3BpY2lvdXMgPSBjYXNlX3doZW4oc2FtcGxlX2FjY2Vzc2lvbiAlaW4lIHN1cyRzYW1wbGVfaWQgfiAnWWVzJykpICU+JSAKICAgICAgICAgIGdncGxvdCguLCBhZXMoLmRhdGFbW3BjRmlyc3RdXSwgLmRhdGFbW3BjU2Vjb25kXV0pKSArCiAgICAgICAgICBnZW9tX3BvaW50KHNpemU9MywgYWVzKGNvbG9yPVN1c3BpY2lvdXMsIHNoYXBlID0gU291cmNlKSkgKwogICAgICAgICAgeGxhYihwYXN0ZTAocGNGaXJzdCwgIjogIixwZXJjZW50VmFyW3N0cl9leHRyYWN0KHBjRmlyc3QsICdcXGQrJykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuaW50ZWdlcigpXSwiJSB2YXJpYW5jZSIpKSArCiAgICAgICAgICB5bGFiKHBhc3RlMChwY1NlY29uZCwgIjogIixwZXJjZW50VmFyW3N0cl9leHRyYWN0KHBjU2Vjb25kLCAnXFxkKycpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5pbnRlZ2VyKCldLCIlIHZhcmlhbmNlIikpICsgCiAgICAgICAgICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKwogICAgICAgICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IDA6MTApICsgZmFjZXRfd3JhcCh+VGlzc3VlKQogICl9CgpgYGAKCjwhLS0gIyBVTUFQIC0tPgo8IS0tIEFkZCBtZXRhIGluZm8gZm9yIHdoZXRoZXIgdGhlIG1ldGFtb1JwaCB0aXNzdWUgbGFiZWxpbmcgc3VzcGVjdHMgYW5kIGlzc3VlICgiU3VzIikgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBjdXN0b20uc2V0dGluZ3MgPSB1bWFwOjp1bWFwLmRlZmF1bHRzIC0tPgo8IS0tIGN1c3RvbS5zZXR0aW5ncyRuX25laWdoYm9ycyA9IDMwIC0tPgo8IS0tIGN1c3RvbS5zZXR0aW5ncyRtZXRyaWMgPC0gJ2V1Y2xpZGVhbicgLS0+CjwhLS0gY3VzdG9tLnNldHRpbmdzJG1pbl9kaXN0IDwtIDAuNSAtLT4KPCEtLSBsaWJyYXJ5KHBsb3RseSkgLS0+CjwhLS0gdW1hcCA8LSB1bWFwOjp1bWFwKFBDQSR4WywxOjIwXSwgY29uZmlnID0gY3VzdG9tLnNldHRpbmdzKSAtLT4KPCEtLSBgYGAgLS0+Cgo8IS0tIGBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTB9IC0tPgo8IS0tIHAgPC0gdW1hcCRsYXlvdXQgJT4lIGFzX3RpYmJsZShyb3duYW1lcyA9ICdzYW1wbGVfYWNjZXNzaW9uJykgJT4lIC0tPgo8IS0tICAgbXV0YXRlKFN1c3BpY2lvdXMgPSBjYXNlX3doZW4oc2FtcGxlX2FjY2Vzc2lvbiAlaW4lIHN1cyRzYW1wbGVfaWQgfiAnWWVzJykpICU+JSAtLT4KPCEtLSAgIGxlZnRfam9pbihkYXRhR0csIGJ5ID0gJ3NhbXBsZV9hY2Nlc3Npb24nKSAlPiUgLS0+CjwhLS0gICBsZWZ0X2pvaW4ocHJlZGljdGlvbnMsIGJ5ID0gYygic2FtcGxlX2FjY2Vzc2lvbiIgPSAic2FtcGxlX2lkIikpICU+JSAtLT4KPCEtLSAgIG11dGF0ZShsYWIgPSAgcGFzdGUwKHByZWRpY3QsICcgKCcsIG1lYW5fbWF4X3Njb3JlLCAnKVxuJywgc2FtcGxlX2FjY2Vzc2lvbiwgJ1xuJywgQWdlKSkgJT4lIC0tPgo8IS0tICAgIyBtdXRhdGUoVGlzc3VlID0gY2FzZV93aGVuKFRpc3N1ZSA9PSAnQnJhaW4nIH4gVGlzc3VlLCAtLT4KPCEtLSAgICMgICAgICAgICAgICAgICAgICAgICAgICAgIENvaG9ydCA9PSAnQm9keScgfiAnQm9keScsIC0tPgo8IS0tICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IFRpc3N1ZSkpICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMoeD1WMSx5PVYyLCBsYWJlbCA9IGxhYikpICsgLS0+CjwhLS0gICBnZW9tX3BvaW50KHNpemU9MywgYWVzKGNvbG9yPVRpc3N1ZSwgc2hhcGUgPSBUaXNzdWUpKSArIC0tPgo8IS0tICAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgLS0+CjwhLS0gICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjpnbGFzYmV5KCksIHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjphbHBoYWJldDIoKSkgJT4lIHVubmFtZSgpKSArIC0tPgo8IS0tICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhwYWxzOjpnbGFzYmV5KCksIHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjphbHBoYWJldDIoKSkgJT4lIHVubmFtZSgpKSArIC0tPgo8IS0tICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IDA6NTApIC0tPgo8IS0tIGdncGxvdGx5KHApIC0tPgo8IS0tIGBgYCAtLT4KCgo8IS0tICMjIFBsb3QgUENBIGFuZCBjb2xvciBieSB0b3RhbCByZWFkcyAtLT4KPCEtLSBXYXMgc3VzcGljaW91cyB0aGF0IFBDMiB3YXMgYmVpbmcgZHJpdmVuIGJ5IHRvdGFsIGNvdW50cyAuLi4gbm9wZSEgTm90IHRoZSBjYXNlIC0tPgo8IS0tIGBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0gLS0+CjwhLS0gcnVuX21ldGEgPC0gcmVhZF9jc3YoJy4uL3NhbG1vbl9jb3VudHMvcnVuX21ldGEuY3N2Lmd6JykgLS0+CjwhLS0gcGNGaXJzdCA8LSAnUEMxJyAtLT4KPCEtLSBwY1NlY29uZCA8LSAnUEMyJyAtLT4KPCEtLSBkYXRhR0cgJT4lIC0tPgo8IS0tICAgI2ZpbHRlcihTb3VyY2UgIT0gJ3NjUk5BJykgJT4lICAtLT4KPCEtLSAgIGFzX3RpYmJsZSgpICU+JSAgLS0+CjwhLS0gICBtdXRhdGUoVGlzc3VlID0gY2FzZV93aGVuKFRpc3N1ZSA9PSAnQnJhaW4nIH4gVGlzc3VlLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29ob3J0ID09ICdCb2R5JyB+ICdCb2R5JywgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBUaXNzdWUpKSAlPiUgIC0tPgo8IS0tICAgbGVmdF9qb2luKCAtLT4KPCEtLSAgICAgcnVuX21ldGEgJT4lIG11dGF0ZShzYW1wbGVfYWNjZXNzaW9uID0gZ3N1Yignc2FsbW9uX3F1YW50XFwvfFxcL2F1eF9pbmZvXFwvbWV0YV9pbmZvLmpzb24nLCcnLGxvZykpKSAlPiUgIC0tPgo8IS0tICAgbXV0YXRlKFN1cyA9IGNhc2Vfd2hlbihzYW1wbGVfYWNjZXNzaW9uICVpbiUgc3VzJHNhbXBsZV9pZCB+ICdZZXMnKSAsIC0tPgo8IS0tICAgICAgICAgIEFnZSA9IGNhc2Vfd2hlbihpcy5uYShBZ2UpIH4gJ05vbmUnLCBUUlVFIH4gQWdlKSwgLS0+CjwhLS0gICAgICAgICAgcHJvY2Vzc2VkID0gbG9nMXAocHJvY2Vzc2VkKSAlPiUgcm91bmQoLiwgZGlnaXRzID0gMCkpICU+JSAgLS0+CjwhLS0gICBnZ3Bsb3QoLiwgYWVzKC5kYXRhW1twY0ZpcnN0XV0sIC5kYXRhW1twY1NlY29uZF1dKSkgKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoc2l6ZT0xLCBhZXMoY29sb3I9bG9nMXAocHJvY2Vzc2VkKSkpICsgLS0+CjwhLS0gICB4bGFiKHBhc3RlMChwY0ZpcnN0LCAiOiAiLHBlcmNlbnRWYXJbc3RyX2V4dHJhY3QocGNGaXJzdCwgJ1xcZCsnKSAlPiUgYXMuaW50ZWdlcigpXSwiJSB2YXJpYW5jZSIpKSArIC0tPgo8IS0tICAgeWxhYihwYXN0ZTAocGNTZWNvbmQsICI6ICIscGVyY2VudFZhcltzdHJfZXh0cmFjdChwY1NlY29uZCwgJ1xcZCsnKSAlPiUgYXMuaW50ZWdlcigpXSwiJSB2YXJpYW5jZSIpKSArICAtLT4KPCEtLSAgIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArIC0tPgo8IS0tICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKyBmYWNldF93cmFwKH5wcm9jZXNzZWQpIC0tPgo8IS0tIGBgYCAtLT4KCjwhLS0gIyBIZWF0bWFwIG9mIG1ldGFtb1JwaCBzY29yaW5nIC0tPgo8IS0tIGBgYHtyLCBmaWcuaGVpZ2h0PTEyMCwgZmlnLndpZHRoPTEwfSAtLT4KPCEtLSBsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKSAtLT4KPCEtLSBobV9kZiA8LSBsZWZ0X2pvaW4ocHJlZGljdGlvbnMsIHByZWRfdmFsdWVzICU+JSBzZWxlY3Qoc2FtcGxlX2lkLCBDb25qdW5jdGl2YTpgVHJhYmVjdWxhciBNZXNod29ya2ApKSAlPiUgZGF0YS5mcmFtZSgpIC0tPgo8IS0tIHJvdy5uYW1lcyhobV9kZikgPC0gaG1fZGYkc2FtcGxlX2lkIC0tPgoKPCEtLSBobV9kZiA8LSBobV9kZiAlPiUgIC0tPgo8IS0tICAgbGVmdF9qb2luKGVtZXRhICU+JSAgLS0+CjwhLS0gICAgICAgICAgICAgICBzZWxlY3Qoc2FtcGxlX2FjY2Vzc2lvbiwgU3ViX1Rpc3N1ZSwgU291cmNlLCBBZ2UpICU+JSB1bmlxdWUoKSwgIC0tPgo8IS0tICAgICAgICAgICAgIGJ5ID0gYygnc2FtcGxlX2lkJz0nc2FtcGxlX2FjY2Vzc2lvbicpKSAgLS0+Cgo8IS0tIGhhX3NpZGUgPSByb3dBbm5vdGF0aW9uKGRmID0gZGF0YS5mcmFtZShTY2llbnRpc3RMYWJlbCA9ICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBobV9kZiRzYW1wbGVfbGFiZWwgJT4lIGZhY3RvcihsZXZlbHMgPSB1bmlxdWUoaG1fZGYkc2FtcGxlX2xhYmVsKSksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlaWdlbkxhYmVsID0gIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhtX2RmJHByZWRpY3QgJT4lIGZhY3RvcihsZXZlbHMgPSB1bmlxdWUoaG1fZGYkc2FtcGxlX2xhYmVsKSksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTb3VyY2UgPSAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG1fZGYkU291cmNlICU+JSBmYWN0b3IobGV2ZWxzID0gdW5pcXVlKGhtX2RmJFNvdXJjZSkpKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdChTY2llbnRpc3RMYWJlbCA9IGMoIkNvbmp1bmN0aXZhIiA9IHBhbHM6OmFscGhhYmV0MigyMClbMl0gJT4lIHVubmFtZSgpLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcm5lYSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzNdICU+JSB1bm5hbWUoKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxlbnMiID0gcGFsczo6YWxwaGFiZXQyKDIwKVs2XSAlPiUgdW5uYW1lKCksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZXRpbmEiID0gcGFsczo6YWxwaGFiZXQyKDIwKVsxMF0gJT4lIHVubmFtZSgpLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUlBFIiA9IHBhbHM6OmFscGhhYmV0MigyMClbMTNdICU+JSB1bm5hbWUoKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyYWJlY3VsYXIgTWVzaHdvcmsiID0gcGFsczo6YWxwaGFiZXQyKDIwKVsxNl0gJT4lIHVubmFtZSgpKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlaWdlbkxhYmVsID0gYygiQ29uanVuY3RpdmEiID0gcGFsczo6YWxwaGFiZXQyKDIwKVsyXSAlPiUgdW5uYW1lKCksICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb3JuZWEiID0gcGFsczo6YWxwaGFiZXQyKDIwKVszXSAlPiUgdW5uYW1lKCksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxlbnMiID0gcGFsczo6YWxwaGFiZXQyKDIwKVs2XSAlPiUgdW5uYW1lKCksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJldGluYSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzEwXSAlPiUgdW5uYW1lKCksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJQRSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzEzXSAlPiUgdW5uYW1lKCksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyYWJlY3VsYXIgTWVzaHdvcmsiID0gcGFsczo6YWxwaGFiZXQyKDIwKVsxNl0gJT4lIHVubmFtZSgpKSkpIC0tPgoKCjwhLS0gSGVhdG1hcChobV9kZlssODoxM10sICAtLT4KPCEtLSAgICAgICAgIHJpZ2h0X2Fubm90YXRpb24gPSBoYV9zaWRlLCAtLT4KPCEtLSAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUpIC0tPgo8IS0tIGBgYCAtLT4KPCEtLSAjIEp1c3QgdGhlICJtaXNsYWJlbGxlZCIgb25lcyAtLT4KCjwhLS0gYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD01fSAtLT4KCjwhLS0gaG1fZGYgPC0gY2JpbmQocHJlZGljdGlvbnMsIG1vZGVsX3Njb3JlcykgLS0+CjwhLS0gcm93Lm5hbWVzKGhtX2RmKSA8LSBobV9kZiRzYW1wbGVfaWQgLS0+Cgo8IS0tIGhtX2RmIDwtIGhtX2RmICU+JSAgLS0+CjwhLS0gICBzZWxlY3QoLXByZWRpY3QpICU+JSAgLS0+CjwhLS0gICBsZWZ0X2pvaW4oY2FsbHMgJT4lIHNlbGVjdChzYW1wbGVfaWQsIHByZWRpY3QpKSAlPiUgIC0tPgo8IS0tICAgbGVmdF9qb2luKGVtZXRhICU+JSAgLS0+CjwhLS0gICAgICAgICAgICAgICBzZWxlY3Qoc2FtcGxlX2FjY2Vzc2lvbiwgU3ViX1Rpc3N1ZSwgU291cmNlLCBBZ2UpICU+JSB1bmlxdWUoKSwgIC0tPgo8IS0tICAgICAgICAgICAgIGJ5ID0gYygnc2FtcGxlX2lkJz0nc2FtcGxlX2FjY2Vzc2lvbicpKSAlPiUgIC0tPgo8IS0tICAgZmlsdGVyKHNhbXBsZV9sYWJlbCAhPSBwcmVkaWN0KSAtLT4KCjwhLS0gaGFfc2lkZSA9IHJvd0Fubm90YXRpb24oZGYgPSBkYXRhLmZyYW1lKFNjaWVudGlzdExhYmVsID0gIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhtX2RmJHNhbXBsZV9sYWJlbCAlPiUgZmFjdG9yKGxldmVscyA9IHVuaXF1ZShobV9kZiRzYW1wbGVfbGFiZWwpKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVpZ2VuTGFiZWwgPSAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG1fZGYkcHJlZGljdCAlPiUgZmFjdG9yKGxldmVscyA9IHVuaXF1ZShobV9kZiRzYW1wbGVfbGFiZWwpKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNvdXJjZSA9ICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBobV9kZiRTb3VyY2UgJT4lIGZhY3RvcihsZXZlbHMgPSB1bmlxdWUoaG1fZGYkU291cmNlKSkpLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KFNjaWVudGlzdExhYmVsID0gYygiQ29uanVuY3RpdmEiID0gcGFsczo6YWxwaGFiZXQyKDIwKVsyXSAlPiUgdW5uYW1lKCksICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29ybmVhIiA9IHBhbHM6OmFscGhhYmV0MigyMClbM10gJT4lIHVubmFtZSgpLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVucyIgPSBwYWxzOjphbHBoYWJldDIoMjApWzZdICU+JSB1bm5hbWUoKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJldGluYSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzEwXSAlPiUgdW5uYW1lKCksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSUEUiID0gcGFsczo6YWxwaGFiZXQyKDIwKVsxM10gJT4lIHVubmFtZSgpLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhYmVjdWxhciBNZXNod29yayIgPSBwYWxzOjphbHBoYWJldDIoMjApWzE2XSAlPiUgdW5uYW1lKCkpLCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVpZ2VuTGFiZWwgPSBjKCJDb25qdW5jdGl2YSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzJdICU+JSB1bm5hbWUoKSwgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcm5lYSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzNdICU+JSB1bm5hbWUoKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVucyIgPSBwYWxzOjphbHBoYWJldDIoMjApWzZdICU+JSB1bm5hbWUoKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmV0aW5hIiA9IHBhbHM6OmFscGhhYmV0MigyMClbMTBdICU+JSB1bm5hbWUoKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUlBFIiA9IHBhbHM6OmFscGhhYmV0MigyMClbMTNdICU+JSB1bm5hbWUoKSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhYmVjdWxhciBNZXNod29yayIgPSBwYWxzOjphbHBoYWJldDIoMjApWzE2XSAlPiUgdW5uYW1lKCkpKSkgLS0+CgoKPCEtLSBIZWF0bWFwKGhtX2RmWyw2OjhdLCAgLS0+CjwhLS0gICAgICAgICByaWdodF9hbm5vdGF0aW9uID0gaGFfc2lkZSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UpIC0tPgo8IS0tIGBgYCAtLT4KCiMgT25lIHBsb3QgcGVyIHN0dWR5IHdpdGggYW55IHBvdGVudGlhbCBtaXMgbGFiZWxzCgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpzdHVkaWVzX3dpdGhfc3VzIDwtIGVtZXRhICU+JSBmaWx0ZXIoc2FtcGxlX2FjY2Vzc2lvbiAlaW4lIHN1cyRzYW1wbGVfaWQpICU+JSBwdWxsKHN0dWR5X2FjY2Vzc2lvbikgJT4lIHVuaXF1ZSgpCgpmdWxsX2RmIDwtIGNiaW5kKHByZWRpY3Rpb25zLCBwcmVkX3ZhbHVlcyAlPiUgc2VsZWN0KENvbmp1bmN0aXZhOmBUcmFiZWN1bGFyLk1lc2h3b3JrYCkpIApmb3IgKGkgaW4gc3R1ZGllc193aXRoX3N1cyl7CiAgI3ByaW50KGkpCiAgaG1fZGYgPC0gbGVmdF9qb2luKHByZWRpY3Rpb25zLCBwcmVkX3ZhbHVlcyAlPiUgc2VsZWN0KHNhbXBsZV9pZCwgQ29uanVuY3RpdmE6YFRyYWJlY3VsYXIuTWVzaHdvcmtgKSAlPiUgdW5pcXVlKCksIGJ5ID0gJ3NhbXBsZV9pZCcpICU+JSAKICAgIGxlZnRfam9pbihlbWV0YSAlPiUgc2VsZWN0KHNhbXBsZV9pZCA9IHNhbXBsZV9hY2Nlc3Npb24sIHN0dWR5X2FjY2Vzc2lvbiwgU3ViX1Rpc3N1ZSwgU291cmNlLCBBZ2UpLCBieSA9J3NhbXBsZV9pZCcpICU+JSAKICAgIGZpbHRlcihzdHVkeV9hY2Nlc3Npb24gJWluJSBpKSAlPiUgCiAgICBkYXRhLmZyYW1lKCkgJT4lIHVuaXF1ZSgpCiAgcm93Lm5hbWVzKGhtX2RmKSA8LSBobV9kZiRzYW1wbGVfaWQKICAKICAKICBoYV9zaWRlID0gcm93QW5ub3RhdGlvbihkZiA9IGRhdGEuZnJhbWUoU2NpZW50aXN0TGFiZWwgPSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBobV9kZiRzYW1wbGVfbGFiZWwgJT4lIGZhY3RvcihsZXZlbHMgPSB1bmlxdWUoZnVsbF9kZiRzYW1wbGVfbGFiZWwpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWlnZW5MYWJlbCA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhtX2RmJHByZWRpY3QgJT4lIGZhY3RvcihsZXZlbHMgPSB1bmlxdWUoZnVsbF9kZiRzYW1wbGVfbGFiZWwpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU291cmNlID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaG1fZGYkU291cmNlICU+JSBmYWN0b3IobGV2ZWxzID0gdW5pcXVlKGhtX2RmJFNvdXJjZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBZ2UgPSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBobV9kZiRBZ2UgJT4lIGZhY3RvcihsZXZlbHMgPSB1bmlxdWUoaG1fZGYkQWdlKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3QoU2NpZW50aXN0TGFiZWwgPSBjKCJDb25qdW5jdGl2YSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzJdICU+JSB1bm5hbWUoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcm5lYSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzNdICU+JSB1bm5hbWUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVucyIgPSBwYWxzOjphbHBoYWJldDIoMjApWzZdICU+JSB1bm5hbWUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmV0aW5hIiA9IHBhbHM6OmFscGhhYmV0MigyMClbMTBdICU+JSB1bm5hbWUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUlBFIiA9IHBhbHM6OmFscGhhYmV0MigyMClbMTNdICU+JSB1bm5hbWUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhYmVjdWxhciBNZXNod29yayIgPSBwYWxzOjphbHBoYWJldDIoMjApWzE2XSAlPiUgdW5uYW1lKCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWlnZW5MYWJlbCA9IGMoIkNvbmp1bmN0aXZhIiA9IHBhbHM6OmFscGhhYmV0MigyMClbMl0gJT4lIHVubmFtZSgpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb3JuZWEiID0gcGFsczo6YWxwaGFiZXQyKDIwKVszXSAlPiUgdW5uYW1lKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGVucyIgPSBwYWxzOjphbHBoYWJldDIoMjApWzZdICU+JSB1bm5hbWUoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZXRpbmEiID0gcGFsczo6YWxwaGFiZXQyKDIwKVsxMF0gJT4lIHVubmFtZSgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJQRSIgPSBwYWxzOjphbHBoYWJldDIoMjApWzEzXSAlPiUgdW5uYW1lKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhYmVjdWxhciBNZXNod29yayIgPSBwYWxzOjphbHBoYWJldDIoMjApWzE2XSAlPiUgdW5uYW1lKCkpKSkKICAKICAKICBwcmludChIZWF0bWFwKGFzLm1hdHJpeChobV9kZlssODoxM10pLCAgY29sPWNpcmNsaXplOjpjb2xvclJhbXAyKGMoMCwgMC41LCAxKSwgYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSksCiAgICAgICAgICAgICAgICByaWdodF9hbm5vdGF0aW9uID0gaGFfc2lkZSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIGNvbHVtbl90aXRsZSA9IGkpKQogIAogIHBjRmlyc3QgPC0gIlBDMSIKICBwY1NlY29uZCA8LSAnUEMyJwogIHByaW50KGRhdGFHRyAlPiUKICAgICAgICAgIGZpbHRlcihzdHVkeV9hY2Nlc3Npb24gPT0gaSwgVGlzc3VlICVpbiUgdGlzc3VlcykgJT4lIAogICAgICAgICAgYXNfdGliYmxlKCkgJT4lIAogICAgICAgICAgbXV0YXRlKFRpc3N1ZSA9IGNhc2Vfd2hlbihUaXNzdWUgPT0gJ0JyYWluJyB+IFRpc3N1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29ob3J0ID09ICdCb2R5JyB+ICdCb2R5JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IFRpc3N1ZSkpICU+JSAKICAgICAgICAgIG11dGF0ZShTdXNwaWNpb3VzID0gY2FzZV93aGVuKHNhbXBsZV9hY2Nlc3Npb24gJWluJSBzdXMkc2FtcGxlX2lkIH4gJ1llcycsIFRSVUUgfiAnTm8nKSkgJT4lIAogICAgICAgICAgZ2dwbG90KC4sIGFlcyguZGF0YVtbcGNGaXJzdF1dLCAuZGF0YVtbcGNTZWNvbmRdXSwgbGFiZWwgPSBzYW1wbGVfYWNjZXNzaW9uKSkgKwogICAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gZGF0YUdHICU+JSAKICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoc3R1ZHlfYWNjZXNzaW9uICE9IGkpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoVGlzc3VlICVpbiUgdGlzc3VlcyksCiAgICAgICAgICAgICAgICAgICAgIHNpemU9MiwgCiAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0uZGF0YVtbcGNGaXJzdF1dLCB5PSAuZGF0YVtbcGNTZWNvbmRdXSksIGNvbG9yID0gJ2dyZXknICkgKwogICAgICAgICAgZ2VvbV9wb2ludChzaXplPTMsIGFlcyhjb2xvcj1TdXNwaWNpb3VzLCBzaGFwZSA9IFNvdXJjZSkpICsKICAgICAgICAgIAogICAgICAgICAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbCggc2l6ZSA9IDIuNSwgbWF4Lm92ZXJsYXBzID0gSW5mKSArCiAgICAgICAgICB4bGFiKHBhc3RlMChwY0ZpcnN0LCAiOiAiLHBlcmNlbnRWYXJbc3RyX2V4dHJhY3QocGNGaXJzdCwgJ1xcZCsnKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5pbnRlZ2VyKCldLCIlIHZhcmlhbmNlIikpICsKICAgICAgICAgIHlsYWIocGFzdGUwKHBjU2Vjb25kLCAiOiAiLHBlcmNlbnRWYXJbc3RyX2V4dHJhY3QocGNTZWNvbmQsICdcXGQrJykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmludGVnZXIoKV0sIiUgdmFyaWFuY2UiKSkgKyAKICAgICAgICAgIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArCiAgICAgICAgICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gMDoxMCkgKyBmYWNldF93cmFwKH5UaXNzdWUsIG5jb2wgPSAyKQogICkKICAKICBwY0ZpcnN0IDwtICJQQzMiCiAgcGNTZWNvbmQgPC0gJ1BDNCcKICBwcmludChkYXRhR0cgJT4lCiAgICAgICAgICBmaWx0ZXIoc3R1ZHlfYWNjZXNzaW9uID09IGksCiAgICAgICAgICAgICAgICAgVGlzc3VlICVpbiUgdGlzc3VlcykgJT4lIAogICAgICAgICAgYXNfdGliYmxlKCkgJT4lIAogICAgICAgICAgbXV0YXRlKFRpc3N1ZSA9IGNhc2Vfd2hlbihUaXNzdWUgPT0gJ0JyYWluJyB+IFRpc3N1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29ob3J0ID09ICdCb2R5JyB+ICdCb2R5JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IFRpc3N1ZSkpICU+JSAKICAgICAgICAgIG11dGF0ZShTdXNwaWNpb3VzID0gY2FzZV93aGVuKHNhbXBsZV9hY2Nlc3Npb24gJWluJSBzdXMkc2FtcGxlX2lkIH4gJ1llcycsIFRSVUUgfiAnTm8nKSkgJT4lIAogICAgICAgICAgZ2dwbG90KC4sIGFlcyguZGF0YVtbcGNGaXJzdF1dLCAuZGF0YVtbcGNTZWNvbmRdXSwgbGFiZWwgPSBzYW1wbGVfYWNjZXNzaW9uKSkgKwogICAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gZGF0YUdHICU+JSAKICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoc3R1ZHlfYWNjZXNzaW9uICE9IGkpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoVGlzc3VlICVpbiUgdGlzc3VlcyksIAogICAgICAgICAgICAgICAgICAgICBzaXplPTIsIAogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9LmRhdGFbW3BjRmlyc3RdXSwgeT0gLmRhdGFbW3BjU2Vjb25kXV0pLCBjb2xvciA9ICdncmV5JyApICsKICAgICAgICAgIGdlb21fcG9pbnQoc2l6ZT0zLCBhZXMoY29sb3I9U3VzcGljaW91cywgc2hhcGUgPSBTb3VyY2UpKSArCiAgICAgICAgICBnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKHNpemUgPSAyLjUsIG1heC5vdmVybGFwcyA9IEluZikgKwogICAgICAgICAgeGxhYihwYXN0ZTAocGNGaXJzdCwgIjogIixwZXJjZW50VmFyW3N0cl9leHRyYWN0KHBjRmlyc3QsICdcXGQrJykgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuaW50ZWdlcigpXSwiJSB2YXJpYW5jZSIpKSArCiAgICAgICAgICB5bGFiKHBhc3RlMChwY1NlY29uZCwgIjogIixwZXJjZW50VmFyW3N0cl9leHRyYWN0KHBjU2Vjb25kLCAnXFxkKycpICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5pbnRlZ2VyKCldLCIlIHZhcmlhbmNlIikpICsgCiAgICAgICAgICBjb3dwbG90Ojp0aGVtZV9jb3dwbG90KCkgKwogICAgICAgICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IDA6MTApICsgZmFjZXRfd3JhcCh+VGlzc3VlLCBuY29sID0gMikKICApCn0KYGBgCgoKCiMgSGFuZCBwaWNrZWQgc2FtcGxlcyB0byByZW1vdmUKClBpY2tlZCBieSB1c2luZyB0aGUgcGxvdHMgYWJvdmUgYW5kIGNoZWNraW5nIG91dCB0aGUgZnVsbCBtZXRhZGF0YSB0byBzZWUgaWYgdGhlcmUgd2VyZSBhbnkgb3RoZXIgZXhwbGFuYXRpb25zIGZvciB3aGF0IGFwcGVhciB0byBiZSBvdXRsaWVycyAoYW4gZXhhbXBsZSBpcyBTUlAxMDU3NTYgd2hpY2ggaGFzIG1hbnkgZWFybHkgZmV0YWwgcmV0aW5hIHRpc3N1ZXMgb3IgU1JQMDk3Njk2IHdoaWNoIGhhcyBhIHN1cnByaXNpbmcgZmxvdyBzb3J0ZWQgcmV0aW5hIHdoaWNoIG9ubHkga2VlcHMgZW5kb3RoZWxpYWwgY2VsbHMpLgpgYGB7cn0KZWlnZW5faGFuZF9yZW1vdmVkIDwtIGMoJ1NSUzY1MDk2ODQnLAogICAgICAgICAgICAgICAgICAgICAgICAnU1JTMzkwNjA5JywKICAgICAgICAgICAgICAgICAgICAgICAgJ1NSUzU0MjU3MycsJ1NSUzU0MjU3NCcsCiAgICAgICAgICAgICAgICAgICAgICAgICdTUlMxNDc4ODM0JywgIwogICAgICAgICAgICAgICAgICAgICAgICAnU1JTODE0NTYwNicsICMKICAgICAgICAgICAgICAgICAgICAgICAgJ1NSUzM2MDEyMycsJ1NSUzM2MDEyNCcsICMKICAgICAgICAgICAgICAgICAgICAgICAgJ1NSUzc1MDQ4NjgnLCdTUlM3NTA0ODY5JywgIwogICAgICAgICAgICAgICAgICAgICAgICAnU1JTNTIzODM1JywnU1JTNTIzODEzJywKICAgICAgICAgICAgICAgICAgICAgICAgJ1NSUzE5NTU0OTQnKQoKcHJlZGljdGlvbnMgJT4lIGxlZnRfam9pbihlbWV0YSAlPiUgc2VsZWN0KHN0dWR5X2FjY2Vzc2lvbiwgc2FtcGxlX2FjY2Vzc2lvbiwgU3ViX1Rpc3N1ZSwgU291cmNlLCBBZ2UpLCBieSA9IGMoJ3NhbXBsZV9pZCc9J3NhbXBsZV9hY2Nlc3Npb24nKSkgJT4lIGFzX3RpYmJsZSgpICU+JSBmaWx0ZXIoc2FtcGxlX2lkICVpbiUgZWlnZW5faGFuZF9yZW1vdmVkKSAlPiUgYXJyYW5nZShzYW1wbGVfbGFiZWwpCgpwcmVkaWN0aW9ucyAlPiUgCiAgbGVmdF9qb2luKGVtZXRhICU+JSB1bmdyb3VwKCkgJT4lICBkcGx5cjo6c2VsZWN0KHN0dWR5X2FjY2Vzc2lvbiwgc2FtcGxlX2FjY2Vzc2lvbiwgU3ViX1Rpc3N1ZSwgU291cmNlLCBBZ2UpLCAKICAgICAgICAgICAgYnkgPSBjKCdzYW1wbGVfaWQnPSdzYW1wbGVfYWNjZXNzaW9uJykpICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgZmlsdGVyKHNhbXBsZV9pZCAlaW4lIGVpZ2VuX2hhbmRfcmVtb3ZlZCkgJT4lIAogIGFycmFuZ2Uoc2FtcGxlX2xhYmVsKSAlPiUgZ3JvdXBfYnkoc2FtcGxlX2xhYmVsLCBTdWJfVGlzc3VlLCBTb3VyY2UsIEFnZSkgJT4lIHN1bW1hcmlzZShDb3VudCA9IG4oKSwgc2FtcGxlcyA9IHBhc3RlMChzYW1wbGVfaWQsIGNvbGxhcHNlID0gJywgJykpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpmb3IgKGkgaW4gYygxLDMsNSw3KSl7CiAgcGNGaXJzdCA8LSBwYXN0ZTAoJ1BDJywgaSkKICBwY1NlY29uZCA8LSBwYXN0ZTAoJ1BDJywgaSsxKQogIHByaW50KGRhdGFHRyAlPiUKICAgICAgICAgIGFzX3RpYmJsZSgpICU+JSAKICAgICAgICAgIG11dGF0ZShUaXNzdWUgPSBjYXNlX3doZW4oVGlzc3VlID09ICdCcmFpbicgfiBUaXNzdWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvaG9ydCA9PSAnQm9keScgfiAnQm9keScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBUaXNzdWUpKSAlPiUgCiAgICAgICAgICBtdXRhdGUoUmVtb3ZlZCA9IGNhc2Vfd2hlbihzYW1wbGVfYWNjZXNzaW9uICVpbiUgZWlnZW5faGFuZF9yZW1vdmVkIH4gJ1llcycpKSAlPiUgCiAgICAgICAgICBnZ3Bsb3QoLiwgYWVzKC5kYXRhW1twY0ZpcnN0XV0sIC5kYXRhW1twY1NlY29uZF1dKSkgKwogICAgICAgICAgZ2VvbV9wb2ludChzaXplPTEsIGFlcyhjb2xvcj1SZW1vdmVkLCBzaGFwZSA9IFNvdXJjZSkpICsKICAgICAgICAgIHhsYWIocGFzdGUwKHBjRmlyc3QsICI6ICIscGVyY2VudFZhcltzdHJfZXh0cmFjdChwY0ZpcnN0LCAnXFxkKycpICU+JSBhcy5pbnRlZ2VyKCldLCIlIHZhcmlhbmNlIikpICsKICAgICAgICAgIHlsYWIocGFzdGUwKHBjU2Vjb25kLCAiOiAiLHBlcmNlbnRWYXJbc3RyX2V4dHJhY3QocGNTZWNvbmQsICdcXGQrJykgJT4lIGFzLmludGVnZXIoKV0sIiUgdmFyaWFuY2UiKSkgKyAKICAgICAgICAgIGNvd3Bsb3Q6OnRoZW1lX2Nvd3Bsb3QoKSArCiAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhwYWxzOjpnbGFzYmV5KCksIHBhbHM6OmFscGhhYmV0MigpLCBwYWxzOjphbHBoYWJldDIoKSkgJT4lIHVubmFtZSgpKSArCiAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKHBhbHM6OmdsYXNiZXkoKSwgcGFsczo6YWxwaGFiZXQyKCksIHBhbHM6OmFscGhhYmV0MigpKSAlPiUgdW5uYW1lKCkpICsKICAgICAgICAgIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSAwOjEwKSArCiAgICAgICAgICBmYWNldF93cmFwKH5UaXNzdWUpCiAgKQp9CgpgYGAKCiMgT3V0cHV0IGhhbmQgcmVtb3ZlZCBJRHMKYGBge3J9CndyaXRlKGVpZ2VuX2hhbmRfcmVtb3ZlZCwgZmlsZSA9ICcuLi9kYXRhL2V4Y2x1ZGVkX3NhbXBsZXMudHh0JykKYGBgCgpgYGB7cn0KZGV2dG9vbHM6OnNlc3Npb25faW5mbygpCmBgYAoKCgo=